﻿using AdvancedPoll.Models;
using AdvancedPoll.ViewModels;
using Orchard;
using Orchard.ContentManagement;
using Orchard.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace AdvancedPoll.Services
{
    public class PollService : IPollService
    {
        private readonly IRepository<PollRecord> _pollRepository;
        private readonly IRepository<PollChoiceRecord> _choiceRepository;
        private readonly IRepository<PollResultRecord> _resultRepository;
        private readonly IRepository<PollVoteRecord> _voteRepository;
        private readonly IOrchardServices _orchardServices;

        public PollService(
            IRepository<PollRecord> pollRepository,
            IRepository<PollChoiceRecord> choiceRepository,
            IRepository<PollResultRecord> resultRepository,
            IRepository<PollVoteRecord> voteRepository,
            IOrchardServices orchardServices)
        {
            _pollRepository = pollRepository;
            _choiceRepository = choiceRepository;
            _resultRepository = resultRepository;
            _orchardServices = orchardServices;
            _voteRepository = voteRepository;
        }

        public Models.PollRecord GetPoll(int id)
        {
            return _pollRepository.Get(id);
        }

        public IQueryable<PollChoiceRecord> GetChoices(int pollId)
        {
            return _choiceRepository.Fetch(c => c.PollPartRecord.Id == pollId).OrderBy(c => c.Id).AsQueryable<PollChoiceRecord>();
        }

        public void CreatePoll(PollPart poll)
        {
            if (poll == null)
                throw new ArgumentNullException("poll");
            var p = new PollRecord
            {
                ContentItemRecord = poll.Record.ContentItemRecord,
                Question = poll.Question,
                OpenDateUtc = poll.OpenDateUtc,
                CloseDateUtc = poll.CloseDateUtc,
                MaxVotes = poll.MaxVotes,
                Shown = poll.IsShown
            };
            _pollRepository.Create(p);
        }


        public IContentQuery<PollPart, PollRecord> GetPolls()
        {
            return _orchardServices.ContentManager
                .Query<PollPart, PollRecord>();
        }


        public void UpdatePoll(PollRecord poll)
        {
            _pollRepository.Update(poll);
        }


        public void CreateChoice(PollChoiceRecord choice)
        {
            _choiceRepository.Create(choice);
            _resultRepository.Create(new PollResultRecord
            {
                Count=0,
                PollChoiceRecord=choice
            });
        }


        public void UpdateChoice(PollChoiceRecord choice)
        {
            _choiceRepository.Update(choice);
        }


        public void DeleteChoice(PollChoiceRecord choice)
        {
            var results = _resultRepository.Fetch(r => r.PollChoiceRecord.Id == choice.Id);
            var votes = _voteRepository.Fetch(v => v.PollChoiceRecord.Id == choice.Id);
            foreach (var r in results)
                _resultRepository.Delete(r);
            foreach (var v in votes)
                _voteRepository.Delete(v);
            _choiceRepository.Delete(choice);
        }


        public IContentQuery<PollPart, PollRecord> GetPolls(bool isShown)
        {
            return _orchardServices.ContentManager
                .Query<PollPart, PollRecord>()
                .Where(poll => poll.Shown == isShown);
        }


        public void OpenPoll(int id)
        {
            var poll = _pollRepository.Get(id);
            poll.Shown = true;
            _pollRepository.Update(poll);
        }

        public void ClosePoll(int id)
        {
            var poll = _pollRepository.Get(id);
            poll.Shown = false;
            _pollRepository.Update(poll);
        }


        public void DeletePoll(int id)
        {
            _orchardServices.ContentManager.Remove(_orchardServices.ContentManager.Get(id));
            _pollRepository.Delete(_pollRepository.Get(id));
        }

        public PollResultRecord GetResult(int choiceId)
        {
            return _resultRepository.Get(r=>r.PollChoiceRecord.Id == choiceId);
        }


        public PollResultViewModel GetPollWithResult(int id)
        {
            PollRecord poll = GetPoll(id);
            PollResultViewModel model = new PollResultViewModel
            {
                Question = poll.Question,
                Shown = poll.Shown,
                Choices = GetChoices(id).Select(c => new PollResultEntry { Choice = c, Result = GetResult(c.Id) }).ToList()
            };
            return model;
        }


        public bool IsPollClosed(int pollId)
        {
            var poll = GetPoll(pollId);
            return DateTime.Now < poll.OpenDateUtc || DateTime.Now > poll.CloseDateUtc;
        }


        public IQueryable<PollVoteRecord> GetVotes(int pollId)
        {
            return _voteRepository.Fetch(v => v.PollPartRecord.Id == pollId).AsQueryable();
        }

        public void EditPoll(PollPart part, PollViewModel model)
        {
            if (part == null)
                throw new ArgumentNullException("part");
            if (model == null)
                throw new ArgumentNullException("model");
            var poll = new PollRecord
            {
                Id = part.Record.ContentItemRecord.Id,
                Question = model.Question,
                OpenDateUtc = model.Open,
                CloseDateUtc = model.Close,
                MaxVotes = model.MaxVotes,
                Shown = model.Shown,
                ContentItemRecord = part.Record.ContentItemRecord
            };
            UpdatePoll(poll);

            foreach (var entry in model.Choices)
            {
                switch (entry.Action)
                {
                    case "Alter":
                        var choice = new PollChoiceRecord
                        {
                            Id = entry.Choice.Id,
                            Answer = entry.Choice.Answer,
                            PollPartRecord = part.Record
                        };
                        UpdateChoice(choice);
                        break;
                    case "Create":
                        if (entry.Choice.Answer != null && entry.Choice.Answer.Length > 0)
                        {
                            choice = new PollChoiceRecord
                            {
                                Answer = entry.Choice.Answer,
                                PollPartRecord = part.Record
                            };
                            CreateChoice(choice);
                        }
                        break;
                }
            }

            var choices = GetChoices(poll.Id).ToList();
            var newChoices = model.Choices.Select(c => choices.FirstOrDefault(x => x.Id == c.Choice.Id || x.Answer == c.Choice.Answer));
            var toDelete = choices.Except(newChoices);
            foreach (var choice in toDelete)
                DeleteChoice(choice);
        }
    }
}